﻿#pragma once
/**
 * @brief 一个简单的 ORM(Object relational mapping),可以自动对数据库中的table进行字段与属性的映射到指定的类型中。
 * @author RayZhang
 * @date 2022/07/04
 * @todo
 * 	集成 leveldb redis 缓存？sql相同的情况下[短时间内]，直接返回？
 */
#include <QString>
#include <QSqlQuery>
// #include <QSqlDataBase>
#include <QtSql/QSqlDatabase>
#include <QSqlError>
// #inc
#include <memory>
// #include "utils.hpp"
#include <QSqlField>
#include <QSqlRecord>
#include <QVector>
#include "../libflow_global.h"
#include "../reflection/reflection.hpp"
// #include "utils/nonecopyable.hpp"
// import libflow.reflection;

namespace flow
{
class LIBFLOW_API dao_t final
{
    // DISABLE_COPY(dao_t)
public:
    using ptr = std::shared_ptr<dao_t>;
    // 获取数据库连接
    dao_t();
    dao_t(const QSqlDatabase& db, const QString& table);
    // 其实将数据变成指针就不用写这些了
    dao_t(const dao_t& rhs) noexcept { *this = rhs; };
    dao_t(dao_t&& rhs) noexcept { *this = std::move(rhs); };
    dao_t& operator=(dao_t&&) noexcept;
    dao_t& operator=(const dao_t& rhs) noexcept;
    explicit dao_t(const QString& table);
    // 克隆一份
    // dao_t clone() const;
    // 归还连接
    ~dao_t();
    /**
     * @brief  获取数据源
     * @return QSqlQuery
     */
    QSqlQuery& get_query();
    QSqlDatabase get_db();
    bool transaction(); // 开始事务操作
    bool commit();      // 提交
    bool rollback();    // 回滚
    // --------------------------------------------------------- Query ---------------------------------
    /**
     * @brief 执行sql语句，将结果存储在result_list中。
     * @return 执行成功或失败
     * @sa
             QVector<User> result_list;
            if (!dao_t::query_raw<User>("select * from user", result_list)) {
                qDebug() << "error";
            }
     */
    template <typename T>
    static bool query_raw(const QString& sql, std::enable_if_t<flow::reflection::is_reflection_v<T>, QVector<T>>& result_list, QString* last_error = nullptr);

    // template <typename T>
    // static bool query_raw(const QString& sql, std::enable_if_t<!flow::reflection::is_reflection_v<T>, QVector<T>>& result_list, QString* last_error = nullptr);
    /**
     * @brief 执行， 添加, 删除, 修改sql语句
     * @return 执行后，影响的行数
     */
    static int exec_raw(const QString& sql, QString* last_error = nullptr);

    /**
     * @brief 设置查询的表格
     * @param [in] table 表名字
     */
    static dao_t from(const QString& table);

    /**
     * @brief 将数据库查询到结果转存到指定的类型中
     * @return 指定类型的对象
     * @sa
        User test4 = dao_t::from("user").get(1).to<User>();
     */
    template <typename T>
    const std::enable_if_t<flow::reflection::is_reflection_v<T>, T> to() const;
    template <typename T>
    const std::enable_if_t<!flow::reflection::is_reflection_v<T>, T> to() const;

    template <typename T>
    bool to(QVector<T>& listT) const;
    /**
    * @brief 获取查询结果中的第一列, 并反序列化
    * @sa
        User user = dao_t::from("user").where("id", "2").first<User>();
        User user = dao_t::from("user").first<User>();
    */
    template <typename T>
    T first();
    /**
     * @brief 查询前limit的行的数据
     */
    const dao_t& get(int limit = 0);
    const dao_t& get(int from, int size);
    const dao_t& find_many(int limit = 0);
    const dao_t& find_many(int from, int size);

    /**
     * \brief 统计
     */
    // SELECT MIN(id) FROM user
    QVariant minimum(const QString& col);
    // 即  SELECT MAX(id) FROM user
    QVariant maximum(const QString& col);
    // 即  SELECT AVG(age) FROM user
    QVariant avg(const QString& col);
    // 即 SELECT AVG(age) FROM user
    QVariant sum(const QString& col);
    // 即 SELECT SUM(age) FROM user
    uint32_t count(const QString& col);
    /***
     * @brief 取值和设置值 主要用于单个记录，即一条数据
     */
    // set后，通过save可以进行更新, dao_t::from("user").where("id = 1").set(key, value).set(key,value)[.create_update()].save();
    dao_t& set(const QString& col, const QVariant& value, int row = 0);
    // 取出某个字段的值, 首先要查询到结果
    QVariant value(int index = 0, int row = 0) const;
    QVariant value(const QString& field, int row = 0) const;
    QVariant operator[](const QString& key) const; // 默认第 0 个
    // dao_t::from("user").where("id", "2").get(1).record(0).toInt();
    const QSqlRecord& const_record(int row = 0) const;
    QSqlRecord& record(int row = 0);
    const QVector<QSqlRecord>& records();
    int size() const; // 当前record的行数
    bool empty() const;
    // --------------------------------------------------------- Delete ---------------------------------
    /**
     * @brief 删除一条记录
     */
    bool remove();
    // --------------------------------------------------------- Update ---------------------------------
    // --------------------------------------------------------- Insert ---------------------------------
    /**
     * @brief 插入记录,更新记录
     */
    // INSERT OR REPLACE INTO tasks (uuid, content) values('id', 'first');
    dao_t& create_insert();
    dao_t& create_update();
    int insert(QVariantMap fields); // 返回最后插入的ID, 插入失败返回 -1
    int update(QVariantMap fields); // 返回影响行数
    int save();

    /**
     * @brief 插入一个结构体
     * @param t 数据结构
     * @param exclude_fields 需要排除的字段
     * @return 成功： 插入的ID， 失败：-1
     */
    template <typename T>
    int insert(const T& t, QVector<QString> exclude_fields = {"id"});
    /**
     * @brief 批量插入一组结构数据, sqlite 根据主键判断是否需要修改
     * @tparam T
     * @param t 经过 REFLECTION 的类型
     * @param exclude_fields 需要排除的字段
     * @return numRowsAffected, 可通过 dao_t::from().max("id") 获取最大的id
     */
    template <typename T>
    int insert_or_update(const QVector<T>& t, QVector<QString> exclude_fields = {"createtime"});

    template <typename T>
    int insert_or_update(const T& t, QVector<QString> exclude_fields = {"createtime"});
    /**
     * @brief 设置日志回调 未测试多线程
     * @param cb  回调
     */
    static void set_on_log(std::function<void(const QString&)> cb);
    /**
     * @brief 设置查询前回调 未测试多线程
     * @param cb 回调
     */
    static void set_on_before_query(std::function<void(const QString&)> cb);
    /**
     * @brief 获取dao_t对象的最后一条错误信息。
     * @return 错误信息
     */
    const QString& last_error();

private:
    template <typename T>
    static void assign(T&& value, const QVariant& data)
    {
        if (!data.isValid()) {
            return;
        }
        using U                   = std::remove_const_t<std::remove_reference_t<T>>;
        constexpr bool is_int64_v = (std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>);
        if constexpr (std::is_integral_v<U> && !is_int64_v) {
            value = data.toInt();
        }
        else if constexpr (is_int64_v) {
            value = data.toLongLong();
        }
        else if constexpr (std::is_floating_point_v<U>) {
            value = data.toDouble();
        }
        else if constexpr (std::is_same_v<QString, U>) {
            value = data.toString();
        }
        else if constexpr (std::is_same_v<QByteArray, U>) {
            value = data.toByteArray();
        }
        else {
            //qDebug() << "this type has not supported yet" << Qt::endl;
        }
    }
    //
public:
    /**
     * @brief where("id=1");
     */
    dao_t& where(const QString& query);
    dao_t& where(const QString& col, const QString& value);
    /**
     * @brief where("id", "<", "2")
     */
    dao_t& where(const QString& col, const QString& op, const QString& value);
    dao_t& where_equal(const QString& col, const QString& value);
    dao_t& where_not_equal(const QString& col, const QString& value);
    dao_t& where_like(const QString& col, const QString& value);
    dao_t& where_not_like(const QString& col, const QString& value);
    /**
     * @brief  where_raw('age > ', 25) // 列名为关键字的话需要 `： where_raw('`order` > ', 25)
     */
    template <typename T>
    dao_t& where_raw(const QString& col, const T& value)
    {
        add_condition(QString("%1'%2'").arg(col).arg(value));
        return *this;
    }
    // 增加 select 的列
    dao_t& select(const QString& col);
    dao_t& select(const QVector<QString>& cols);

    dao_t& limit(int limit);

    dao_t& order_by_expr(const QString& expr);
    dao_t& order_by_asc(const QString& col);
    dao_t& order_by_desc(const QString& col);

    // 重新查询
    dao_t& refresh();
    // 返回构造的sql语句
    QString build();

protected:
    enum class QueryType
    {
        None,
        Select = 1,
        Update,
        Delete,
        Insert
    };
    // void set_connection_object(MYSQL *mysql);
protected:
    void set_with_no_dirt(const QString& key, const QString& value);

    void set_query_type(QueryType query_type);
    void set_table(const QString& table);
    void set_limit(int limit);
    void set_size(int size);

    void add_result_column(const QString& col);
    void add_condition(const QString& col, const QString& op, const QString& value);
    void add_condition(const QString& condition);
    void add_order_by(const QString& col, const QString& order);
    void add_group_by(const QString& col);

    QString build_result_columns() const;
    QString build_conditions() const;
    QString build_field_keys();
    QString build_field_values();
    QString build_changes();
    QString build_order_by();
    QString build_group_by();

    QString build_from();
    QString build_into();

    QString build_limit();

    QString build_select();
    QString build_update();
    QString build_delete();
    QString build_insert();

    void dirt_field(const QString& field_name);
    void clean_dirty_fields();

    /* SELECT, UPDATE, INSERT, DELETE backends */
    bool find_values();
    int update_records();
    int insert_record();
    int remove_records();
    void set_record_fields(QVariantMap fields);
    bool query(const QString& sql);

    static void set_error(const QSqlQuery& err_query, QString* err_text = nullptr);

private:
    QueryType _query_type = QueryType::None;

    QString _table;
    uint32_t _limit = 0;
    uint32_t _size  = 0;

    QVector<QString> _conditions;
    QVector<QString> _results;
    QVector<QString> _dirty_fields;
    QVector<QString> _order_by;
    QVector<QString> _group_by;

    QVector<QSqlRecord> _listRecord;
    QSqlDatabase _db;
    QSqlQuery _sql_query;

    QString _last_error_text;

    // 日志回调
    inline static std::function<void(const QString&)> _on_before_query = nullptr;
    inline static std::function<void(const QString&)> _on_log          = nullptr;
};

inline void dao_t::set_size(int size)
{
    _size = size;
}

template <typename T>
inline bool flow::dao_t::to(QVector<T>& listT) const
{
    for (int row = 0; row < size(); ++row) {
        T t;
        flow::reflection::for_each(t, [&t, row, this](auto item, auto i) {
            const std::string_view name = flow::reflection::get_name<T>(i);
            auto v                      = this->const_record(row).value(name.data());
            if (v.isValid()) {
                assign(t.*item, v);
            }
        });
        listT.push_back(t);
    }
    return !listT.isEmpty();
}

//  运行指定的查询字符串。
template <typename T>
inline bool flow::dao_t::query_raw(const QString& sql, std::enable_if_t<flow::reflection::is_reflection_v<T>, QVector<T>>& row_list, QString* last_error /*= nullptr*/)
{
    dao_t mapper;
    auto query = mapper.get_query();
    if (query.exec(sql)) {
        while (query.next()) {
            T t;
            flow::reflection::for_each(t, [&t, &query](auto item, auto i) {
                const std::string_view name = flow::reflection::get_name<T>(i);
                auto v                      = query.value(name.data());
                assign(t.*item, v);
            });
            row_list.push_back(t);
        }
        return true;
    }
    set_error(query);
    return false;
}

// template <typename T>
// inline bool flow::dao_t::query_raw(const QString& sql,
//	std::enable_if_t<!flow::reflection::is_reflection_v<T>,
//	QVector<T>>& row_list,
//	QString* last_error /*= nullptr*/)
//{
//	using namespace epr;
//	// 编译期断言
//	static_assert(reflection::is_tuple<T>::value);
//	constexpr auto SIZE = std::tuple_size_v<T>;
//	dao_t mapper;
//	auto query = mapper.get_query();
//	if (query.exec(sql)) {
//		while (query.next()) {
//			T tp = {};
//			int index = 0;
//			reflection::for_each(
//				tp,
//				[this, &index](auto& item, auto I) {
//					if constexpr (iguana::is_reflection_v<decltype(item)>) {
//						std::remove_reference_t<decltype(item)> t = {};
//						reflection::for_each(t, [this, &index, &t, &query](auto ele, auto i) {
//							std::string_view name = flow::reflection::get_name<T>(i);
//							auto v = query.value(name.data());
//							assign(t.*ele, v);
//							});
//						item = std::move(t);
//					}
//				},
//				std::make_index_sequence<SIZE>{});
//			if (index > 0)
//				v.push_back(std::move(tp));
//		}
//		return true;
//	}
//	return false;
// }
template <typename T>
inline T flow::dao_t::first()
{
    get(1);
    return to<T>();
}

template <typename T>
inline int dao_t::insert(const T& t, QVector<QString> exclude_fields)
{
    create_insert();
    auto& record = this->record();
    flow::reflection::for_each(t, [&t, this, &record, &exclude_fields](auto item, auto i) {
        QString name = QString::fromStdString(flow::reflection::get_name<T>(i).data());
        if (!exclude_fields.contains(name)) {
            auto v = record.value(name);
            this->set(name, t.*item);
        }
    });
    return save();
}
template <typename T>
int flow::dao_t::insert_or_update(const T& t, QVector<QString> exclude_fields /*= { "createtime" }*/)
{
    create_insert();
    auto& record = this->record();
    flow::reflection::for_each(t, [&t, this, &record, &exclude_fields](auto item, auto i) {
        QString name = QString::fromStdString(flow::reflection::get_name<T>(i).data());
        if (!exclude_fields.contains(name)) {
            auto v = record.value(name);
            this->set(name, t.*item);
        }
    });
    QString query = "REPLACE ";
    query += build_into() + " (" + build_field_keys() + ") VALUES (" + build_field_values() + ")";
    // 进行批处理，如果出错就输出错误
    transaction();
    if (!_sql_query.exec(query)) {
        set_error(_sql_query);
        rollback();
        return 0;
    }
    // qDebug() << _sql_query.executedQuery();
    commit();
    return _sql_query.numRowsAffected();
}
template <typename T>
inline int dao_t::insert_or_update(const QVector<T>& list_record, QVector<QString> exclude_fields)
{
    if (list_record.size() == 1) {
        return insert_or_update(list_record.first(), exclude_fields);
    }
    if (list_record.empty()) {
        return 0;
    }
    // 批处理
    QString field_names;
    QString placeholder;
    // 存每一个字段的值
    constexpr int FIELD_SIZE = flow::reflection::get_size<T>();
    std::map<QString, QVariantList> field_values;
    for (int row = 0; row < list_record.size(); ++row) {
        const auto& t = list_record[row];
        int index     = 0;
        flow::reflection::for_each(t, [&](auto item, auto i) {
            // field_values[index].resize(FIELD_SIZE);
            const QString name = QString::fromStdString(flow::reflection::get_name<T>(i).data());
            if (!exclude_fields.contains(name)) {
                if (0 == row) {
                    field_names += QString(R"('%1')").arg(name) + ", ";
                    placeholder += (":" + name + ",");
                }
                field_values[name] << t.*item;
            }
        });
    }
    field_names.chop(2);
    placeholder.chop(1);

    if (const QString sql = QString("replace into %1(%2) values(%3)").arg(_table).arg(field_names).arg(placeholder); !_sql_query.prepare(sql)) {
        set_error(_sql_query);
        return 0;
    }
    //_sql_query.prepare("insert into user(name, age) values (?, ?)");
    for (const auto& [key, value] : field_values) {
        _sql_query.bindValue(QString(":" + key), value);
    }

    // 进行批处理，如果出错就输出错误
    transaction();
    if (!_sql_query.execBatch()) {
        // qDebug() << _sql_query.executedQuery();
        set_error(_sql_query);
        rollback();
        return 0;
    }
    // qDebug() << _sql_query.executedQuery();
    commit();
    return _sql_query.numRowsAffected();
}

template <typename T>
const std::enable_if_t<flow::reflection::is_reflection_v<T>, T> flow::dao_t::to() const
{
    T t;
    const auto& record = this->const_record();
    if (!record.isEmpty()) {
        flow::reflection::for_each(t, [&t, this, &record](auto item, auto i) {
            std::string_view name = flow::reflection::get_name<T>(i);
            if (record.contains(name.data())) {
                auto v = record.value(name.data());
                assign(t.*item, v);
            }
        });
    }
    return t;
}
template <typename T>
std::enable_if_t<!flow::reflection::is_reflection_v<T>, T> const flow::dao_t::to() const
{
    static_assert(reflection::is_tuple<T>::value);
    constexpr auto SIZE = std::tuple_size_v<T>;

    T tp               = {};
    const auto& record = this->const_record();
    flow::reflection::for_each(
        tp,
        [this, &record](auto& item, auto I) {
            // 映射的字段才进行处理 std::tuple<User, int>
            // 这里需要遍历 User 的结构
            if constexpr (flow::reflection::is_reflection_v<decltype(item)>) {
                std::remove_reference_t<decltype(item)> t = {};
                reflection::for_each(t, [this, &t, &record](auto ele, auto i) {
                    std::string_view name = flow::reflection::get_name<T>(i);
                    if (record.contains(name.data())) {
                        auto v = record.value(name.data());
                        assign(t.*ele, v);
                    }
                });
                item = std::move(t);
            }
            else {
                // 直接赋值
                assign(item, record.value(I));
            }
        },
        std::make_index_sequence<SIZE>{});
    return tp;
}
} // namespace flow
